/*
 * Copyright the original author or authors.
 * 
 * Licensed under the MOZILLA PUBLIC LICENSE, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.mozilla.org/MPL/MPL-1.1.html
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.kairos.utils 
{
	import flash.events.Event;
	
	import com.bourre.events.BasicEvent;
	import com.bourre.events.EventBroadcaster;
	import com.bourre.log.PixlibStringifier;
	import com.bourre.transitions.TickListener;
	import com.kairos.engine.RTObject;  

	/**
	 * A <code>Clock</code> object is usefull to measure time 
	 * in an game build with a <code>RTBeacon</code>.
	 * 
	 * <p>A <code>Clock</code> object can also be used to create
	 * time limited game. Just pass a value in the constructor
	 * to set the end time of the clock. The time is set in milliseconds.</p>
	 * 
	 * <p><code>Clock</code> time scale is affected by the <code>RTBeacon</code> global
	 * speed and max step size.</p>
	 *	
	 * @author	Cédric Néhémie	
	 * @example Using a clock to retreive the elapsed time in an animation
	 * <listing>import com.kairos.utils.Clock;
	 * 
	 * var myClock : Clock = new Clock ();
	 * 
	 * myClock.start();
	 * 
	 * function onTick ()
	 * {
	 * 	trace ( myClock.elapsed() );
	 * }
	 * </listing>
	 * 
	 * Using a clock object to get a call back at the end.
	 * <listing>import com.kairos.utils.Clock;
	 * 
	 * var myClock : Clock = new Clock ( 10000 );
	 * 
	 * myClock.addEventListener ( Clock.onClockEndEvent, this );
	 * 
	 * myClock.start ();
	 * 
	 * function onClockEnd ()
	 * {
	 * 	trace ( "Clock ended after : " + myClock.elapsed() + " ms" );
	 * } </listing>
	 * 
	 * Using a clock to display the running time in a game.
	 * <listing>var myClock : Clock = new Clock ();
	 * 
	 * myClock.start();
	 * 
	 * function onTick ()
	 * {
	 * 	var d : Date = myClock.toDate();
	 * 	trace ( d.getMinutes() + ":" + d.getSeconds() + "." + d.getMilliseconds() );
	 * }
	 * </listing>
	 */
	public class Clock extends RTObject
	{
		// Elapsed time
		protected var _nE : Number;
		// Max time
		protected var _nLimit : Number;
		// Event to broadcast
		protected var _eOnClockEndEvent : BasicEvent;
		
		/**
		 * Specific event dispatched by the Clock when timer go past the limit.
		 * 
		 * @eventType	onClockEnd
		 */
		public static const onClockEndEVENT : String = "onClockEnd";
	
		/**
		 * Create a new <code>Click</code> object.
		 * 
		 * <p>The optional argument let you set a time limit in milliseconds 
		 * before the clock dispatch the <code>onClockEndEVENT</code> event. 
		 * If you not set the limit or if you set it to 0, the clock never 
		 * dispatch any event.</p>
		 * 
		 * @param	limit	The max time before the clock dispatch the
		 * <code>onClockEndEVENT</code> event.
		 */
		public function Clock ( limit : uint = 0 )
		{
			_oEB = new EventBroadcaster( this );
			_eOnClockEndEvent = new BasicEvent( onClockEndEVENT, this );
			
			_nE = 0;
			setLimit ( limit );
		} 
		
		/**
		 * Realize the time measurement.
		 * 
		 * <p>If a limit have been defined, and if elapsed time is greater than
		 * the limit, the end event is dispatched.</p>
		 */
		public override function onTick ( e : Event = null ) : void
		{
			_nE += getLocalStep( e );
			
			if( _nLimit == 0 ) return;
			
			if( _nE > _nLimit )
			{
				 _oEB.broadcastEvent ( _eOnClockEndEvent );
				 fireCommandEndEvent();
				 stop();
			}
		}
		
		/**
		 * Set a time limit before the clock dispatch the <code>onClockEndEVENT</code>
		 * event. If you not set the limit or if you set it to 0, the clock 
		 * never dispatch any event.
		 *	
		 * @param	limit	The max time before the clock dispatch the
		 * <code>onClockEndEVENT</code> event.
		 */
		public function setLimit ( limit : uint = 0 ) : void
		{
			_nLimit = !isNaN( limit ) ? limit : 0 ;
		}
		
		/**
		 * Return the current time limit of the <code>Clock</code> object.
		 * 	
		 * @return Number of milliseconds before the clock stop.	
		 */
		public function getLimit () : Number
		{
			return _nLimit;
		}
		
		/**
		 * Returns the total time elapsed in milliseconds since <code>Clock</code>
		 * start.
		 *	
		 * @return <code>Number</code>
		 */	
		public function elapsed () : Number
		{
			return _nE;
		}
		
		/**
		 * Adds listener for receiving all events.
		 * 
		 * @param oL Listener object.
		 * @example Adding an object as listener of the Clock
		 * <listing>t.addListener( myListener );</listing>
		 */
		public function addListener( oL : Object ) : void
		{
			_oEB.addListener( oL );
		}
		
		/**
		 * Removes listener for receiving all events.
		 * 
		 * @param oL Listener object.
		 * @example Removing an object as listener of the Clock
		 * <listing>t.removeListener( myListener );</listing>
		 */
		public function removeListener( oL : Object ) : void
		{
			_oEB.removeListener( oL );
		}
		
		/**
		 * Adds listener for specifical event.
		 * 
		 * @param t Name of the Event.
		 * @param oL Listener object.
		 * @param rest Additional parameters for proxying method listener.
		 * @example Adding an object as listener for the <code>onClockEndEVENT</code>
		 * <listing>t.addEventListener( Clock.onClockEndEVENT, myListener );</listing>
		 */
		public function addEventListener( e : String, oL : Object, ... rest ) : void
		{
			_oEB.addEventListener.apply( _oEB, [ e, oL ].concat( rest ) );
		}
		
		/**
		 * Removes listener for specifical event.
		 *   
		 * @param t Name of the Event.
		 * @param oL Listener object.
		 * @example Removing an object as listener for the <code>onClockEndEVENT</code>
		 * <listing>t.removeEventListener( Clock.onClockEndEVENT, myListener );</listing>
		 */
		public function removeEventListener( e : String, oL : Object ) : void
		{
			_oEB.removeEventListener( e, oL );
		}
		/**
		 * Returns a <code>Date</code> object based on the <code>Clock</code> elapsed time.
		 *	
		 * @return	 Date of the elapsed time of the clock object.
		 */
		public function toDate () : Date
		{
			return new Date ( 0, 0, 0, 0, 0, 0, elapsed() );
		}
		
		/**
		 * Returns a <code>Date</code> object based on the <code>Clock</code> remain time.
		 *	
		 * @return	 Date of the remain time of the clock object.
		 */
		public function toDateRemain () : Date
		{
			return new Date ( 0, 0, 0, 0, 0, 0, remain() );
		}
		
		/**
		 * Returns the remaining time until the time limit.
		 * 
		 * <p>If the clock have no time limit the function returns 
		 * the elapsde time instead.</p>
		 * 
		 * @return remaining time until clock end
		 */
		public function remain () : Number
		{
			if( _nLimit != 0 ) return _nLimit - _nE;
			else return _nE;
		}
		
		/**
		 * Returns the String representation of the object.
		 */	
		public override function toString() : String
		{
			return PixlibStringifier.stringify( this );
		}
	}
}